#ifndef DES_H_INCLUDED
#define DES_H_INCLUDED

#include <iostream>
#include <string>

using namespace std;

class DES
{
private:
    string source, key;
    string C[16],D[16];
    string newKeys[16];
    string getNewKey(string key)
    {
        int s1[] =
        {
            57, 49, 41, 33, 25, 17, 9,
            1, 58, 50, 42, 34, 26, 18,
            10, 2, 59, 51, 43, 35, 27,
            19, 11, 3, 60, 52, 44, 36,
            63, 55, 47, 39, 31, 23, 15,
            7, 62, 54, 46, 38, 30, 22,
            14, 6, 61, 53, 45, 37, 29,
            21, 13, 5, 28, 20, 12, 4
        };
        string res = "";
        for(int i = 0; i < 56; i++)
        {
            res += key.at(s1[i] - 1);
        }
        return res;
    }
    void dealShift(string key)
    {
        int s[] = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};
        string C0 = key.substr(0,28);
        string D0 = key.substr(28,28);
        C[0] = C0.substr(s[0], 28 - s[0]);
        C[0] += C0.substr(0,s[0]);
        D[0] = D0.substr(s[0], 28 - s[0]);
        D[0] += D0.substr(0,s[0]);
        for(int i = 1; i < 16; i++)
        {
            C[i] = C[i - 1].substr(s[i], 28 - s[i]);
            C[i] += C[i - 1].substr(0,s[i]);
            D[i] = D[i - 1].substr(s[i], 28 - s[i]);
            D[i] += D[i - 1].substr(0,s[i]);
        }
    }
    void getNewKey()
    {
        int s[]=
        {
            14, 17, 11, 24, 1, 5,
            3, 28, 15, 6, 21, 10,
            23, 19, 12, 4, 26, 8,
            16, 7, 27, 20, 13, 2,
            41, 52, 31, 37, 47, 55,
            30, 40, 51, 45, 33, 48,
            44, 49, 39, 56, 34, 53,
            46, 42, 50, 36, 29, 32
        };
        string temp;
        for(int i = 0; i < 16; i++)
        {
            newKeys[i] = "";
            temp = "";
            temp += C[i];
            temp += D[i];
            for(int j = 0; j < 48; j++)
            {
                newKeys[i] += temp.at(s[j] - 1);
            }
        }
    }
    string M2IP()
    {
        int s1[] =
        {
            58,50,42,34,26,18,10,2,
            60,52,44,36,28,20,12,4,
            62,54,46,38,30,22,14,6,
            64,56,48,40,32,24,16,8,
            57,49,41,33,25,17,9,1,
            59,51,43,35,27,19,11,3,
            61,53,45,37,29,21,13,5,
            63,55,47,39,31,23,15,7
        };
        string res = "";
        for(int i = 0; i < 64; i++)
        {
            res += source.at(s1[i] - 1);
        }
        return res;
    }
    string XOR(string a, string b)
    {
        string res="";
        for(int i = 0; i < a.size(); i++)
        {
            res += a.at(i) == b.at(i) ? "0" : "1";
        }
        return res;
    }
    string E(string s)
    {
        int s1[] =
        {
            32,1,2,3,4,5,
            4,5,6,7,8,9,
            8,9,10,11,12,13,
            12,13,14,15,16,17,
            16,17,18,19,20,21,
            20,21,22,23,24,25,
            24,25,26,27,28,29,
            28,29,30,31,32,1
        };
        string res = "";
        for(int i = 0; i < 48; i++)
        {
            res += s.at(s1[i] - 1);
        }
        return res;
    }
    string dec2bin(int dec)
    {
        switch(dec)
        {
        case 0 :
        {
            return string("0000");
        }
        case 1 :
        {
            return string("0001");
        }
        case 2 :
        {
            return string("0010");
        }
        case 3 :
        {
            return string("0011");
        }
        case 4 :
        {
            return string("0100");
        }
        case 5 :
        {
            return string("0101");
        }
        case 6 :
        {
            return string("0110");
        }
        case 7 :
        {
            return string("0111");
        }
        case 8 :
        {
            return string("1000");
        }
        case 9 :
        {
            return string("1001");
        }
        case 10 :
        {
            return string("1010");
        }
        case 11 :
        {
            return string("1011");
        }
        case 12 :
        {
            return string("1100");
        }
        case 13 :
        {
            return string("1101");
        }
        case 14 :
        {
            return string("1110");
        }
        case 15 :
        {
            return string("1111");
        }
        }
    }
    int bin2dec(string n)
    {
        int res = 0;
        int pow = 1;
        for(int i = n.size() - 1; i >= 0 ; i--)
        {
            if(n.at(i) == '1')
            {
                res += pow;
            }
            pow*=2;
        }
        return res;
    }
    string f(string r, int keyIndex)
    {
        string step1 = XOR(E(r),newKeys[keyIndex]);
        string addresses[8];
        for(int i = 0; i < 8; i++)
        {
            addresses[i] = step1.substr( (6*i),6);
        }
        int S[8][4][16]=
        {
            {
                //1
                {
                    14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7
                },
                {
                    0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8
                },
                {
                    4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0
                },
                {
                    15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13
                }
            },
            {
                //2
                {
                    15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10
                },
                {
                    3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5
                },
                {
                    0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15
                },
                {
                    13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9
                }
            },{//3
                {
                    10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8
                },
                {
                    13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1
                },
                {
                    13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7
                },
                {
                    1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12
                }
            },{//4
                {
                    7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15
                },
                {
                    13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9
                },
                {
                    10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4
                },
                {
                    3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14
                }
            },{//5
                {
                    2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9
                },
                {
                    14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6
                },
                {
                    4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14
                },
                {
                    11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3
                }
            },{//6
                {
                    12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11
                },
                {
                    10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8
                },
                {
                    9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6
                },
                {
                    4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13
                }
            },{//7
                {
                    4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1
                },
                {
                    13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6
                },
                {
                    1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2
                },
                {
                    6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12
                }
            },{//8
                {
                    13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7
                },
                {
                    1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2
                },
                {
                    7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8
                },
                {
                    2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
                }
            }
        };
        string res1 = "";
        for(int i = 0; i < 8; i++)
        {
            res1 += dec2bin(S[i][
                                bin2dec(addresses[i].substr(0,1).append(addresses[i].substr(5,1)))
                            ][
                                bin2dec(addresses[i].substr(1,4))
                            ]);
        }
        int s2[] = {16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10,2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25};
        string res = "";
        for(int i = 0; i < 32; i++)
        {
            res += res1.at(s2[i] - 1);
        }
        return res;
    }
    string encrypt(string IP)
    {
        string L = IP.substr(0,32),R = IP.substr(32,32);
        string temp;
        for(int i = 0; i < 16; i++)
        {
            temp = L;
            L = R;
            R = XOR(temp, f(R,i));
        }
        return R+L;
    }
    string decrypt(string IP)
    {
        string L = IP.substr(0,32),R = IP.substr(32,32);
        string temp;
        for(int i = 15; i >= 0; i--)
        {
            temp = L;
            L = R;
            R = XOR(temp, f(R,i));
        }
        return R+L;
    }
    string lastTrans(string s)
    {
        int n[] = { 40, 8, 48, 16, 56, 24, 64, 32,
                    39, 7, 47, 15, 55, 23, 63, 31,
                    38, 6, 46, 14, 54, 22, 62, 30,
                    37, 5, 45, 13, 53, 21, 61, 29,
                    36, 4, 44, 12, 52, 20, 60, 28,
                    35, 3, 43, 11, 51, 19, 59, 27,
                    34, 2, 42, 10, 50, 18, 58, 26,
                    33, 1, 41, 9, 49, 17, 57, 25
                  };
        string res = "";
        for(int i = 0; i < 64; i++)
        {
            res += s.at(n[i] - 1);
        }
        return res;
    }
public:
    DES(string source, string key)
    {
        if(!(source.length() == 64 && key.length() == 64))
        {
            cout<<"please check the size of source or key (both of them should be 64(bin) or 16(hex))"<<endl;
            system("exit");
        }
        this -> source = source;
        this -> key = key;
    }
    string getSource()
    {
        return source;
    }
    string getCiphertext()
    {
        string temp = getNewKey(key);
        dealShift(temp);
        getNewKey();
        temp = M2IP();
        temp = encrypt(temp);
        temp = lastTrans(temp);
        return temp;
    }
    string getExplicitly(){
        string temp = getNewKey(key);
        dealShift(temp);
        getNewKey();
        temp = M2IP();
        temp = decrypt(temp);
        temp = lastTrans(temp);
        return temp;
    }
};

#endif // DES_H_INCLUDED

